/*
 * jxController Project (2014)
 * 
 * by Denilson Edinaldo Pinto
 * 
 * LGPL
 * Copyright (C) 2014
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.jxController.event;

import java.awt.Frame;

import javax.swing.JOptionPane;

import org.jxController.i18n.I18N;
import org.jxController.view.CrudInternalFrame;
import org.jxController.view.FindDialog;
import org.jxController.view.ReportDialog;
import org.jxDao.dao.Dao;
import org.jxDao.dao.Persistable;
import org.jxDao.dao.exception.JXDaoException;
import org.jxDao.dao.filter.FilterUtil;
import org.jxDao.dao.filter.Filterable;
import org.jxDao.dao.filter.OrderBy;
import org.jxSwing.component.dialog.ExceptionDialog;
import org.jxSwing.component.dialog.WaitDialog;

/**
 * @author Denilson Edinaldo Pinto
 * @porject jxController
 * @package org.jxController.event
 * @date 06/05/2014 - 08:56:12
 * @param <Bean>
 * @param <TView>
 * @param <TDao>
 */
public class GenericCrudEvent<Bean extends Persistable, TView extends CrudInternalFrame<Bean>, TDao extends Dao<Bean>> implements CrudEvent {
	
	private final TView view;
	
	private final TDao dao;
	
	private boolean compareWithLastVersionInDB = true;
	
	private Class<?>[] findFetch = null;
	
	/**
	 * @param view
	 * @param dao
	 */
	public GenericCrudEvent(final TView view, final TDao dao) {
	
		this.view = view;
		this.dao = dao;
		
		//
		if(getView().getCrudEvent() == null) {
			
			getView().setCrudEvent(this);
		}
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxSwing.component.bar.CrudBar#deleteAction()
	 */
	@Override
	public boolean deleteAction() {
	
		//
		if(JOptionPane.showConfirmDialog(getView(), I18N.controller().reallyWantDeleteThisResult()) == JOptionPane.YES_OPTION) {
			
			//
			return new WaitDialog<Integer>() {
				
				@Override
				protected Integer waitWhileThisIsBeingDone() {
				
					int result = 0;
					
					try {
						//
						result = getDao().deleteByPK(getView().getBean());
						
						//
						if(result > 0) {
							
							getView().setBean(null);
						}
						
					}catch(final Throwable throwable) {
						
						try {
							//
							getDao().rollbackTransaction();
							
						}catch(final Throwable throwable2) {
							
							throwable.addSuppressed(throwable2);
						}
						
						ExceptionDialog.showMessage(throwable);
					}
					
					return result;
				}
			}.getResult() > 0;
			
		}
		
		return false;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#editAction()
	 */
	@Override
	public void editAction() {
	
		getView().makeOldBean();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#findAction(java.lang.Class<?>[])
	 */
	@Override
	public boolean findAction(final Class<?>... fetch) {
	
		findFetch = fetch;
		
		return findAction((Filterable)null, fetch);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#findAction(org.jxDao.dao.filter.OrderBy, java.lang.Class[])
	 */
	@Override
	public boolean findAction(OrderBy orderBy, Class<?>... fetch) {
		
		findFetch = fetch;
		
		return findAction(FilterUtil.orderBy(orderBy), fetch);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#findAction(org.jxDao.getDao().filter.Filterable, java.lang.Class<?>[])
	 */
	@Override
	public boolean findAction(final Filterable filter, final Class<?>... fetch) {
	
		findFetch = fetch;
		
		final boolean[] selected = {false};
		
		final FindDialog<Bean, TDao> dialog = new FindDialog<Bean, TDao>((Frame)null, getView().getTitle(), getDao(), filter) {
			
			private static final long serialVersionUID = -1157894315300853321L;
			
			@Override
			public void selectedBeanAction(Bean selectedBean) {
			
				try {
					//
					getView().setBean(getDao().fillWithFetch(selectedBean, findFetch));
					
					selected[0] = true;
					
				}catch(JXDaoException jxDaoException) {
					
					ExceptionDialog.showMessage(jxDaoException);
				}
			}
		};
		
		dialog.setIconImage(getView().getFrameIcon() != null ? getView().getFrameIcon().getImage() : null);
		
		return selected[0];
	}
	
	/**
	 * @return the dao
	 */
	public final TDao getDao() {
	
		return dao;
	}
	
	/**
	 * @return the view
	 */
	public final TView getView() {
	
		return view;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxSwing.component.bar.CrudBar#newAction()
	 */
	@Override
	public void newAction() {
	
		getView().setBean(null);
		
		getView().firstFocus();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxSwing.component.bar.CrudBar#printAction()
	 */
	@Override
	public void printAction() {
	
		printAction((Class<?>[])null);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#printAction(java.lang.Class<?>[])
	 */
	@Override
	public void printAction(final Class<?>... fetch) {
	
		printAction((Filterable)null, fetch);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#printAction(org.jxDao.dao.filter.OrderBy, java.lang.Class[])
	 */
	@Override
	public void printAction(OrderBy orderBy, Class<?>... fetch) {
	
		printAction(FilterUtil.orderBy(orderBy), fetch);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#printAction(org.jxDao.getDao().filter.Filterable, java.lang.Class<?>[])
	 */
	@Override
	public void printAction(final Filterable filter, final Class<?>... fetch) {
	
		final ReportDialog<Bean, TDao> dialog = new ReportDialog<>((Frame)null, getView().getTitle(), getDao(), filter, fetch);
		
		dialog.setIconImage(getView().getFrameIcon() != null ? getView().getFrameIcon().getImage() : null);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#refreshAction()
	 */
	@Override
	public void refreshAction() {
	
		//
		if(getView().isShowRefreshButton()) {
			
			final Bean bean = getView().getBean();
			
			//
			if(bean != null) {
				
				//
				new WaitDialog<Void>() {
					
					@Override
					protected Void waitWhileThisIsBeingDone() {
					
						try {
							// Obtém a última versão do objeto no Banco e atribui a última versão do objeto
							getView().setBean(getDao().getByPK(bean, findFetch));
							
						}catch(final Throwable throwable) {
							
							ExceptionDialog.showMessage(throwable);
						}
						
						return null;
					}
				};
			}
		}
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxSwing.component.bar.CrudBar#saveAction()
	 */
	@Override
	public boolean saveAction(final Class<?>... fetch) {
	
		final Bean beanInView = getView().getBeanInView();
		
		// Se exite um Bean em tela
		if(beanInView != null) {
			
			//
			return new WaitDialog<Integer>() {
				
				@Override
				protected Integer waitWhileThisIsBeingDone() {
				
					int result = 0;
					
					try {
						// Se não existia versão antiga, então é uma Inclusão
						if(getView().getOldBean() == null) {
							
							result = getDao().save(beanInView, fetch);
							
							// Atribui a última versão do objeto
							getView().setBean(beanInView);
							
						}else {
							
							// Se o Bean foi alterado
							if( !beanInView.equals(getView().getOldBean())) {
								
								// Se o usuário concorda em salvar as alterações
								if(JOptionPane.showConfirmDialog(getView(), I18N.controller().reallyWantSaveThisResult()) == JOptionPane.YES_OPTION) {
									
									try {
										//
										Bean lastBean = null;
										
										// Se compara o Bean em tela com a última versão dele que está na Base de Dados
										if(compareWithLastVersionInDB) {
											
											// Obtém a última versão do objeto no Banco
											lastBean = getDao().getByPK(beanInView, findFetch);
											
											// Se o Bean já foi excluido por outro usuário
											if(lastBean == null) {
												
												JOptionPane.showMessageDialog(getView(), I18N.controller().recordHasBeenDeletedBySomeone(), org.jxSwing.i18n.I18N.component().warning(), JOptionPane.WARNING_MESSAGE);
												
												result = 1;
												
											}else {
												
												// Se a útima versão do Bean que está na Base de Dados é
												// igual ao Bean que estava em tela antes do usuário realizar
												// as alterações, então permite salvar as alterações
												if(lastBean.equals(getView().getOldBean())) {
													
													result = getDao().save(beanInView, fetch);
													
													lastBean = beanInView;
													
												}else {
													
													JOptionPane.showMessageDialog(getView(), I18N.controller().recordHasBeenChangedBySomeone(), org.jxSwing.i18n.I18N.component().warning(), JOptionPane.WARNING_MESSAGE);
													
													result = 1;
												}
											}
											
										}else {
											
											result = getDao().save(beanInView, fetch);
											
											lastBean = beanInView;
										}
										
										// Atribui a última versão do objeto
										getView().setBean(lastBean);
										
									}catch(final Throwable throwable) {
										
										try {
											//
											getDao().rollbackTransaction();
											
										}catch(final Throwable throwable2) {
											
											throwable.addSuppressed(throwable2);
										}
										
										// Atribui a versão antiga do objeto
										getView().setBean(beanInView);
										
										ExceptionDialog.showMessage(throwable);
									}
									
								}else {
									
									undoAction();
									
									result = 1;
								}
								
							}else {
								
								result = 1;
							}
						}
						
					}catch(final Throwable throwable) {
						
						try {
							//
							getDao().rollbackTransaction();
							
						}catch(final Throwable throwable2) {
							
							throwable.addSuppressed(throwable2);
						}
						
						// Atribui a versão antiga do objeto
						getView().setBean(beanInView);
						
						ExceptionDialog.showMessage(throwable);
					}
					
					return result;
				}
			}.getResult() > 0;
			
		}else {
			
			getView().setBean(null);
			
			return true;
		}
	}
	
	/**
	 * @param compareWithLastVersionInDB
	 *        the compareWithLastVersionInDB to set
	 */
	public final void setCompareWithLastVersionInDB(final boolean compareWithLastVersionInDB) {
	
		this.compareWithLastVersionInDB = compareWithLastVersionInDB;
	}
	
	/**
	 * @param validate
	 *        the compareWithLastVersionInDB to set
	 * @see #setCompareWithLastVersionInDB(boolean)
	 */
	@Deprecated
	public final void setValidateBeanVersionInSaveAction(final boolean validate) {
	
		setCompareWithLastVersionInDB(validate);
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jxController.event.CrudEvent#cleanAction()
	 */
	@Override
	public void undoAction() {
	
		//
		new WaitDialog<Void>() {
			
			@Override
			protected Void waitWhileThisIsBeingDone() {
			
				try {
					//
					Bean lastBean = null;
					
					// Se compara o Bean em tela com a última versão dele que está na Base de Dados
					if(compareWithLastVersionInDB && getView().getOldBean() != null) {
						
						// Obtém a última versão do objeto no Banco
						lastBean = getDao().getByPK(getView().getOldBean(), findFetch);
						
						// Se o Bean já foi excluido por outro usuário
						if(lastBean == null) {
							
							JOptionPane.showMessageDialog(getView(), I18N.controller().recordHasBeenDeletedBySomeone(), org.jxSwing.i18n.I18N.component().warning(), JOptionPane.WARNING_MESSAGE);
						}
						
					}else {
						
						lastBean = getView().getOldBean();
					}
					
					// Atribui a última versão do objeto
					getView().setBean(lastBean);
					
				}catch(final Throwable throwable) {
					
					try {
						//
						getDao().rollbackTransaction();
						
					}catch(final Throwable throwable2) {
						
						throwable.addSuppressed(throwable2);
					}
					
					// Atribui a versão antiga do objeto
					getView().setBean(getView().getOldBean());
					
					ExceptionDialog.showMessage(throwable);
				}
				
				return null;
			}
		};
	}
}
